001 /* 002 * Copyright 2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.metro.builder; 020 021 import java.io.IOException; 022 import java.net.URL; 023 import java.net.URI; 024 import java.net.URISyntaxException; 025 import java.util.Properties; 026 import java.beans.IntrospectionException; 027 028 import net.dpml.lang.Version; 029 030 import net.dpml.metro.info.Type; 031 import net.dpml.metro.info.InfoDescriptor; 032 import net.dpml.metro.info.ContextDescriptor; 033 import net.dpml.metro.info.CategoryDescriptor; 034 import net.dpml.metro.info.ServiceDescriptor; 035 import net.dpml.metro.info.EntryDescriptor; 036 import net.dpml.metro.info.CollectionPolicy; 037 import net.dpml.metro.info.LifestylePolicy; 038 import net.dpml.metro.info.ThreadSafePolicy; 039 import net.dpml.metro.info.Priority; 040 041 import net.dpml.state.State; 042 import net.dpml.state.StateDecoder; 043 import net.dpml.state.DefaultState; 044 045 import net.dpml.util.DOM3DocumentBuilder; 046 import net.dpml.util.DecodingException; 047 import net.dpml.util.ElementHelper; 048 import net.dpml.util.Resolver; 049 050 import org.w3c.dom.Element; 051 import org.w3c.dom.Document; 052 053 054 /** 055 * Component type decoder. 056 * 057 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 058 * @version 1.0.1 059 */ 060 public final class ComponentTypeDecoder 061 { 062 private static final StateDecoder STATE_DECODER = new StateDecoder(); 063 064 private static final DOM3DocumentBuilder DOCUMENT_BUILDER = new DOM3DocumentBuilder(); 065 066 /** 067 * Load a type. 068 * @param subject the component implementation class 069 * @param resolver build-time value resolver 070 * @return the component type descriptor 071 * @exception IOException if an error occurs reading the type definition 072 * @exception IntrospectionException if an introspection error occurs 073 */ 074 public Type loadType( Class subject, Resolver resolver ) throws IOException, IntrospectionException 075 { 076 String classname = subject.getName(); 077 String path = classname.replace( '.', '/' ); 078 String target = path + ".type"; 079 ClassLoader classloader = subject.getClassLoader(); 080 URL url = classloader.getResource( target ); 081 if( null == url ) 082 { 083 return Type.createType( subject ); 084 } 085 else 086 { 087 try 088 { 089 URI uri = new URI( url.toString() ); 090 return loadType( uri, resolver ); 091 } 092 catch( URISyntaxException e ) 093 { 094 final String error = 095 "Bad uri value." 096 + "\nURI: " + url; 097 IOException exception = new IOException( error ); 098 exception.initCause( e ); 099 throw exception; 100 } 101 } 102 } 103 104 /** 105 * Load a type. 106 * @param uri the component type source uri 107 * @param resolver build-time uri resolver 108 * @return the component type descriptor 109 * @exception IOException if an error occurs reading the type definition 110 */ 111 public Type loadType( URI uri, Resolver resolver ) throws IOException 112 { 113 try 114 { 115 final Document document = DOCUMENT_BUILDER.parse( uri ); 116 final Element root = document.getDocumentElement(); 117 return buildType( root, resolver ); 118 } 119 catch( Throwable e ) 120 { 121 final String error = 122 "An error while attempting to load a type." 123 + "\nType URI: " + uri; 124 IOException exception = new IOException( error ); 125 exception.initCause( e ); 126 throw exception; 127 } 128 } 129 130 private Type buildType( Element root, Resolver resolver ) throws Exception 131 { 132 InfoDescriptor info = buildNestedInfoDescriptor( root ); 133 ServiceDescriptor[] services = buildNestedServices( root ); 134 ContextDescriptor context = buildNestedContext( root ); 135 CategoryDescriptor[] categories = buildNestedCategories( root ); 136 State state = buildNestedState( root ); 137 return new Type( info, categories, context, services, state ); 138 } 139 140 private InfoDescriptor buildNestedInfoDescriptor( Element root ) throws DecodingException 141 { 142 Element info = ElementHelper.getChild( root, "info" ); 143 if( null == info ) 144 { 145 final String error = 146 "Definition of <type> is missing the required <info> element."; 147 throw new DecodingException( root, error ); 148 } 149 150 String name = ElementHelper.getAttribute( info, "name" ); 151 String classname = ElementHelper.getAttribute( info, "class" ); 152 String version = ElementHelper.getAttribute( info, "version" ); 153 String collection = ElementHelper.getAttribute( info, "collection", "system" ); 154 String lifestyle = ElementHelper.getAttribute( info, "lifestyle", null ); 155 String threadsafe = ElementHelper.getAttribute( info, "threadsafe", "unknown" ); 156 Properties properties = buildNestedProperties( info ); 157 158 return new InfoDescriptor( 159 name, 160 classname, 161 Version.parse( version ), 162 buildLifestylePolicy( lifestyle ), 163 CollectionPolicy.parse( collection ), 164 ThreadSafePolicy.parse( threadsafe ), 165 properties ); 166 } 167 168 private LifestylePolicy buildLifestylePolicy( String lifestyle ) 169 { 170 if( null == lifestyle ) 171 { 172 return null; 173 } 174 else 175 { 176 return LifestylePolicy.parse( lifestyle ); 177 } 178 } 179 180 private ServiceDescriptor[] buildNestedServices( Element root ) 181 { 182 Element element = ElementHelper.getChild( root, "services" ); 183 if( null == element ) 184 { 185 return new ServiceDescriptor[0]; 186 } 187 else 188 { 189 Element[] children = ElementHelper.getChildren( element, "service" ); 190 ServiceDescriptor[] services = new ServiceDescriptor[ children.length ]; 191 for( int i=0; i<children.length; i++ ) 192 { 193 Element child = children[i]; 194 String classname = ElementHelper.getAttribute( child, "class" ); 195 String version = ElementHelper.getAttribute( child, "version", "1.0.0" ); 196 services[i] = new ServiceDescriptor( classname, Version.parse( version ) ); 197 } 198 return services; 199 } 200 } 201 202 private ContextDescriptor buildNestedContext( Element root ) 203 { 204 Element context = ElementHelper.getChild( root, "context" ); 205 if( null == context ) 206 { 207 return new ContextDescriptor( new EntryDescriptor[0] ); 208 } 209 else 210 { 211 Element[] children = ElementHelper.getChildren( context, "entry" ); 212 EntryDescriptor[] entries = new EntryDescriptor[ children.length ]; 213 for( int i=0; i<children.length; i++ ) 214 { 215 Element child = children[i]; 216 String key = ElementHelper.getAttribute( child, "key" ); 217 String classname = ElementHelper.getAttribute( child, "class" ); 218 boolean optional = ElementHelper.getBooleanAttribute( child, "optional", false ); 219 boolean isVolatile = ElementHelper.getBooleanAttribute( child, "volatile", false ); 220 entries[i] = new EntryDescriptor( key, classname, optional, isVolatile ); 221 } 222 return new ContextDescriptor( entries ); 223 } 224 } 225 226 private CategoryDescriptor[] buildNestedCategories( Element root ) 227 { 228 Element element = ElementHelper.getChild( root, "categories" ); 229 if( null == element ) 230 { 231 return new CategoryDescriptor[0]; 232 } 233 else 234 { 235 Element[] children = ElementHelper.getChildren( element, "category" ); 236 CategoryDescriptor[] categories = new CategoryDescriptor[ children.length ]; 237 for( int i=0; i<children.length; i++ ) 238 { 239 Element elem = children[i]; 240 String name = ElementHelper.getAttribute( elem, "name" ); 241 String priority = ElementHelper.getAttribute( elem, "priority" ); 242 Properties properties = buildNestedProperties( elem ); 243 categories[i] = new CategoryDescriptor( name, Priority.parse( priority ), properties ); 244 } 245 return categories; 246 } 247 } 248 249 private State buildNestedState( Element root ) 250 { 251 Element element = ElementHelper.getChild( root, "state" ); 252 if( null == element ) 253 { 254 return new DefaultState(); 255 } 256 else 257 { 258 return STATE_DECODER.buildStateGraph( element ); 259 } 260 } 261 262 private Properties buildNestedProperties( Element element ) 263 { 264 Element[] entries = ElementHelper.getChildren( element, "property" ); 265 if( entries.length == 0 ) 266 { 267 return null; 268 } 269 else 270 { 271 Properties properties = new Properties(); 272 for( int i=0; i<entries.length; i++ ) 273 { 274 Element elem = entries[i]; 275 String name = ElementHelper.getAttribute( elem, "name" ); 276 String value = ElementHelper.getAttribute( elem, "value" ); 277 properties.setProperty( name, value ); 278 } 279 return properties; 280 } 281 } 282 283 } 284